
package base.Skills;

import base.Elements;
import base.ScriptLoader;
import base.Status;
import base.Character;
import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetManager;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import java.io.IOException;
import java.util.EnumSet;
import javax.script.Invocable;
import javax.script.ScriptEngine;

/**
 * @author Lucas Nunes
 */

public class Skill implements Savable {
    private final String NAME = "name";
    private final String DESCRIPTION = "description";
    private final String ELEMENTS = "elements";
    private final String STATUS = "status";
    private final String TYPE = "type";
    private final String TARGET = "target";
    private final String ON_CAST = "onCast";
    private final String ON_HIT_CHARACTER = "onHitCharacter";
    private final String ON_HIT_WORLD = "onHitWorld";
    private final String GET_VITALITY = "vitality";
    private final String GET_SPIRIT = "spirit";
    private final String GET_PHYSIC_ATTACK = "physicAttack";
    private final String GET_PHYSIC_DEFENSE = "physicDefense";
    private final String GET_PHYSIC_DAMAGE = "physicDamage";
    private final String GET_RANGE_ATTACK = "rangeAttack";
    private final String GET_RANGE_DEFENSE = "rangeDefense";
    private final String GET_RANGE_DAMAGE = "rangeDamage";
    private final String GET_MAGIC_ATTACK = "magicAttack";
    private final String GET_MAGIC_DEFENSE = "magicDefense";
    private final String GET_MAGIC_DAMAGE = "magicDamage";
    private final String GET_PRECISION = "precision";
    private final String GET_ATTACK_SPEED = "attackSpeed";
    private final String GET_MOVEMENT_SPEED = "movementSpeed";
    
    private String assetName;
    private ScriptEngine engine;
    private Invocable script;
    
    private int useCount = 0;
    
    public static Skill loadScript(AssetManager assetManager, String assetName) {
        return new Skill(assetName, (Invocable) assetManager.loadAsset(assetName));
    }
    
    public Skill(String assetName, Invocable script) {
        this.assetName = assetName;
        this.engine = (ScriptEngine) script;
        this.script = script;
    }
    
    public void use() {
        useCount++;
    }
    
    public int getLevel() {
        return this.useCount / 10000;
    }
    
    public void setLevel(int level) {
        this.useCount = level * 10000;
    }

    
    // Script Wrappers
    public String getName() {
        return ScriptLoader.getValueOrDefault(engine, NAME, assetName + ":Missing name");
    }

    public String getDescription() {
        return ScriptLoader.getValueOrDefault(engine, DESCRIPTION, assetName + ":Missing description");
    }

    public EnumSet<Elements> getElements() {
        return ScriptLoader.getValueOrDefault(engine, ELEMENTS, EnumSet.noneOf(Elements.class));
    }

    public EnumSet<Status> getStatus() {
        return ScriptLoader.getValueOrDefault(engine, STATUS, EnumSet.noneOf(Status.class));
    }
    
    public String getType() {
        return ScriptLoader.getValueOrDefault(engine, TYPE, "undefined");
    }
    
    public boolean isTarget() {
        return ScriptLoader.getValueOrDefault(engine, TARGET, false);
    }
    
    public void onCast(Character character) {
        ScriptLoader.invokeFunctionOrDefault(script, ON_CAST, null, this, character);
    }
    
    public void onHitCharacter(Character castChar, Character hitChar) {
        ScriptLoader.invokeFunctionOrDefault(script, ON_HIT_CHARACTER, null, this, castChar, hitChar);
    }
    
    public void onHitWorld(Character character) {
        ScriptLoader.invokeFunctionOrDefault(script, ON_HIT_WORLD, null, this, character);
    }

    public int getVitality(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_VITALITY, 0, this, character);
    }

    public int getSpirit(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_SPIRIT, 0, this, character);
    }

    public int getPhysicAttack(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_PHYSIC_ATTACK, 0, this, character);
    }

    public int getPhysicDefense(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_PHYSIC_DEFENSE, 0, this, character);
    }
    
    public int getPhisicDamage(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_PHYSIC_DAMAGE, 0, this, character);
    }

    public int getRangeAttack(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_RANGE_ATTACK, 0, this, character);
    }

    public int getRangeDefence(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_RANGE_DEFENSE, 0, this, character);
    }

    public int getRangeDamage(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_RANGE_DAMAGE, 0, this, character);
    }

    public int getMagicAttack(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_MAGIC_ATTACK, 0, this, character);
    }

    public int getMagicDefence(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_MAGIC_DEFENSE, 0, this, character);
    }
    
    public int getMagicDamage(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_MAGIC_DAMAGE, 0, this, character);
    }

    public int getPrecision(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_PRECISION, 0, this, character);
    }

    public int getAttackSpeed(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_ATTACK_SPEED, 0, this, character);
    }

    public int getMovementSpeed(Character character) {
        return ScriptLoader.invokeFunctionOrDefault(script, GET_MOVEMENT_SPEED, 0, this, character);
    }

    public void write(JmeExporter ex) throws IOException {
        OutputCapsule out = ex.getCapsule(this);
        
        out.write(assetName, "assetName", null);
        out.write(useCount, "useCount", 0);
    }

    public void read(JmeImporter im) throws IOException {
        InputCapsule in = im.getCapsule(this);
        
        assetName = in.readString("assetName", null);
        script = im.getAssetManager().loadAsset(new AssetKey<Invocable>(assetName));
        useCount = in.readInt("useCount", 0);
    }
}

